/**
 * @file netspeed.c
 * netspeed source for TrayInfo.
 */

/* netspeed support added by Laszlo Ashin <kodest@gmail.com> */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "trayinfo.h"

/** Source handle. */
static source_handle_t *netspeed_sh;

/** Maxumal number of measured netspeed devices. */
#define NETSPEED_NOFDEVS	4

static double netspeed_sumtime;
static char *netspeed_devices;

/** Linked list item of a network history element. */
typedef struct hist_s {
	struct hist_s *next;

	/** Time of the measure. */
	double time;

	/** Measured values. */
	struct { unsigned recved, sent; } val[NETSPEED_NOFDEVS];
} hist_t;

static hist_t *first, *last;

static int
netspeed_write(double now, int fd)
{
	hist_t *hp;
	double timul;
	char sipref[] = " kMG";
	char buf[256];
	int i = 0, j;
	int n = 0;
	FILE *f = fopen("/proc/net/dev", "r");

	if (!f) return 0;

	{
		double timeout = now - netspeed_sumtime;

		while (first) {
			if (first->time > timeout) break;
			hp = first;
			first = hp->next;
			free(hp);
		}
		if (!first) last = NULL;
	}

	hp = (hist_t *)malloc(sizeof(hist_t));
	if (!hp) goto fclo;
	memset(hp, 0, sizeof(hist_t));
	hp->next = NULL;
	hp->time = now;
	if (last) {
		last->next = hp;
		timul = 1.0f / (hp->time - first->time);
	} else {
		first = hp;
		timul = 1.0f;
	}
	last = hp;

	while (i < NETSPEED_NOFDEVS && fgets(buf, sizeof(buf), f) == buf) {
		char *s, *t;
		unsigned recved, sent;
		double down, up;
		int k;

		for (s = buf; *s && *s < 33; ++s);
		t = strchr(s, ':');
		if (!t) continue;
		*t++ = '\0';

		/* skip unneeded ifaces */
		if (netspeed_devices && !strstr(netspeed_devices, s))
			continue;
/*		if (*s == 'l' && s[1] == 'o' && !s[2]) continue;*/

		if (sscanf(t, "%u %*u %*u %*u %*u %*u %*u %*u %u",
		    &recved, &sent) != 2) continue;
		/* skip inactive iface */
		if (!recved && !sent) continue;
		last->val[i].recved = recved;
		last->val[i].sent = sent;
		down = recved - first->val[i].recved;
		up = sent - first->val[i].sent;
		++i;
		if (last == first) continue;
		down *= timul, up *= timul;

		/* don't display iface with no traffic */
		if (down < 1.0 && up < 1.0) continue;
		for (j = 0; j + 1 < sizeof(sipref); ++j) {
			if (down < 1000) break;
			down /= 1024.0f;
		}
		for (k = 0; k + 1 < sizeof(sipref); ++k) {
			if (up < 1000) break;
			up /= 1024.0f;
		}
		n += show("%s:%3.0f%c/%3.0f%c", s, down, sipref[j], up, sipref[k]);
	}
fclo:	fclose(f);
	return n;
}


static int
netspeed_init(source_handle_t *sh)
{
	netspeed_sumtime = 5.0f;
	netspeed_devices = NULL;
	first = last = NULL;
	return 0;
}

static void
netspeed_done(void)
{
	if (netspeed_devices) free(netspeed_devices);
	netspeed_devices = NULL;

	while (first) {
		hist_t *p = first;
		first = p->next;
		free(p);
	}
	last = NULL;
}

static int
netspeed_cfg_sumtime(const char *s)
{
	double d;

	if (sscanf(s, "%lf", &d) != 1) {
		return 1;
	}
	netspeed_sumtime = d;
	return 0;
}

static int
netspeed_cfg_devices(const char *s)
{
	if (netspeed_devices) free(netspeed_devices);
	netspeed_devices = strdup(s);
	return 0;
}

int
init_module(module_phase_t phase)
{
	switch (phase) {
	case MODULE_PHASE_INIT: {
		source_t source = {
			.name = "netspeed",
			.init = netspeed_init,
			.done = netspeed_done,
			.write = netspeed_write,
		};
		netspeed_sh = register_source(&source);
		if (netspeed_sh == NULL) {
			return !0;
		}
		register_config_handler("netspeed-sumtime", netspeed_cfg_sumtime);
		register_config_handler("netspeed-devices", netspeed_cfg_devices);
		break;
	}
	case MODULE_PHASE_EXIT:
		forget_config_handler("netspeed-devices", netspeed_cfg_devices);
		forget_config_handler("netspeed-sumtime", netspeed_cfg_sumtime);
		forget_source(netspeed_sh);
		break;
	}
}
